package dispatchers

import (
	"context"
	"fmt"
	"net/http"
	"strings"
	"sync"
	"time"

	"gitee.com/bitwormhole/mails-pnp-driver/lib/common/dto"
	"gitee.com/bitwormhole/mails-pnp-driver/lib/common/vo"
	"github.com/starter-go/application"
	"github.com/starter-go/httpagent"
	"github.com/starter-go/mails"
	"github.com/starter-go/vlog"
)

// Engine ...
type Engine struct {

	//starter:component

	MailToService mails.Service     //starter:inject("#")
	WebClients    httpagent.Clients //starter:inject("#")

	PullFromURL   string //starter:inject("${mails.pnp.dispatcher.pull.url}")
	PullWithToken string //starter:inject("${mails.pnp.dispatcher.pull.token}")

	mutex sync.Mutex
}

// Life ...
func (inst *Engine) Life() *application.Life {
	return &application.Life{
		OnStart: inst.start,
		OnLoop:  inst.loop,
	}
}

func (inst *Engine) start() error {
	go func() {
		err := inst.run()
		if err != nil {
			vlog.Error(err.Error())
		}
	}()
	return nil
}

func (inst *Engine) loop() error {
	for {
		time.Sleep(time.Second)
	}
}

func (inst *Engine) run() error {
	ctx := context.Background()
	for {
		msglist, err := inst.pull(ctx)
		if err == nil {
			err = inst.dispatchAll(ctx, msglist)
			inst.handleError(err)
		} else {
			inst.handleError(err)
			time.Sleep(time.Second * 5)
		}
	}
}

func (inst *Engine) pull(c context.Context) (*vo.Message, error) {

	url := inst.PullFromURL
	req := &httpagent.Request{
		Context: c,
		Method:  http.MethodPost,
		URL:     url,
	}
	client := inst.WebClients.GetClient()

	resp, err := client.Execute(req)
	if err != nil {
		return nil, err
	}

	ent, err := resp.GetEntity()
	if err != nil {
		return nil, err
	}

	body2 := &vo.Message{}
	err = ent.ReadJSON(body2)
	if err != nil {
		return nil, err
	}

	return body2, nil
}

func (inst *Engine) dispatchAll(c context.Context, all *vo.Message) error {
	go func() {
		c2 := context.Background()
		for _, item := range all.Items {
			err := inst.dispatchItem(c2, item)
			inst.handleError(err)
		}
	}()
	return nil
}

func (inst *Engine) dispatchItem(c context.Context, item *dto.Message) error {

	inst.mutex.Lock()
	defer inst.mutex.Unlock()

	msg2, err := inst.convertMessage(item)
	if err != nil {
		return err
	}

	return inst.MailToService.Send(c, msg2)
}

func (inst *Engine) parseAddress(addr string) (mails.Address, error) {

	bad := fmt.Errorf("bad mail address '%s'", addr)
	parts := strings.Split(addr, "@")

	if len(parts) != 2 {
		return "", bad
	}

	p0 := strings.TrimSpace(parts[0])
	p1 := strings.TrimSpace(parts[1])
	if p0 == "" || p1 == "" {
		return "", bad
	}

	addr2 := (p0 + "@" + p1)
	return mails.Address(addr2), nil
}

func (inst *Engine) convertMessage(src *dto.Message) (*mails.Message, error) {

	from, _ := inst.parseAddress(src.FromAddr)
	to, _ := inst.parseAddress(src.ToAddr)

	from = inst.mapAddressFrom(from)

	dst := &mails.Message{
		Title:       src.Title,
		ToAddresses: []mails.Address{to},
		FromAddress: from,
		ContentType: src.ContentType,
		Content:     []byte(src.Content),
	}
	return dst, nil
}

func (inst *Engine) mapAddressFrom(src mails.Address) mails.Address {

	// 临时实现：

	// table := map[mails.Address]mails.Address{
	// 	"email@pnp": "email@smtp",
	// 	"sms@pnp":   "sms@phone",
	// }

	// dst := table[src]
	// if dst == "" {
	// 	dst = src
	// }
	// return dst

	return src
}

func (inst *Engine) handleError(err error) {
	if err == nil {
		return
	}
	vlog.Error(err.Error())
}
